package com.basic.common.utils.easyExcel;


import cn.idev.excel.EasyExcel;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.metadata.CellExtra;
import cn.idev.excel.write.metadata.WriteSheet;
import cn.idev.excel.write.style.HorizontalCellStyleStrategy;
import com.basic.common.config.BasicConfig;
import com.basic.common.utils.DateUtils;
import com.basic.common.utils.StringUtils;
import com.basic.common.utils.file.FileUploadUtils;
import com.basic.common.utils.uuid.ULID;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Excel操作类
 *
 * @author zhaoxy_jn@163.com
 */
@Slf4j
public class EasyExcelUtils {

    /**
     * Excel通用导出方法
     *
     * @param <T>       数据类型
     * @param clazz     数据对象的Class
     * @param sheetName sheet名
     * @param data      数据列表
     * @return
     * @throws RuntimeException 当写入过程中发生异常时抛出
     */
    public static <T> String excelExport(Class<T> clazz, String sheetName, List<T> data) throws IOException {
        validateInput(clazz, sheetName, null);
        return writeExcel(clazz, sheetName, data, null,null);
    }

    /**
     * Excel导出指定的列
     *
     * @param <T> 数据类型
     * @param clazz 数据对象的Class
     * @param sheetName sheet名
     * @param data 数据列表
     * @param columnNames 指定的列名集合
     * @throws RuntimeException 当写入过程中发生异常时抛出
     */
    public static <T> void excelExport(Class<T> clazz, String sheetName, List<T> data, Set<String> columnNames) throws IOException {
        validateInput(clazz, sheetName, null);
        writeExcel(clazz, sheetName, data, columnNames,null);
    }

    /**
     * Excel动态头导出
     *
     * @param <T> 数据类型
     * @param sheetName sheet名
     * @param heads 表头列表
     * @param data 数据列表
     * @throws RuntimeException 当写入过程中发生异常时抛出
     */
    public static <T> void excelExport(String sheetName, List<String> heads, List<T> data) throws IOException {
        validateInput(null, sheetName, null);
        writeExcel(null, sheetName, data, null, heads);
    }

    /**
     * Excel多个sheet页导出
     *
     * @param <T> 数据类型
     * @param fileName 文件名
     * @param list 包含多个sheet信息的列表
     * @throws RuntimeException 当写入过程中发生异常时抛出
     */
    public static <T> void excelPort(String fileName, List<SheetInfoBean> list) {
        validateInput(null, null, null, list);
        try (ExcelWriter excelWriter = EasyExcel.write(fileName).build()) {
            if (excelWriter != null) {
                for (SheetInfoBean bean : list) {
                    try {
                        WriteSheet writeSheet = EasyExcel.writerSheet(bean.getSheetName()).head(bean.getHeadClass()).build();
                        excelWriter.write(bean.getDataList(), writeSheet);
                    } catch (Exception e) {
                        log.error("导出Excel失败，原因：", e);
                        throw new RuntimeException("导出Excel失败", e);
                    }
                }
            }
        } catch (Exception e) {
            log.error("创建Excel写入器失败", e);
            throw new RuntimeException("创建Excel写入器失败", e);
        }
    }

    /**
     * Excel通用写
     * @param clazz 数据对象的Class
     * @param sheetName sheet名
     * @param data 数据列表
     * @param columnNames 列名
     * @param heads 表头
     */
    private static <T> String writeExcel(Class<T> clazz, String sheetName, List<T> data, Set<String> columnNames, List<String> heads) throws IOException {
        String realFileName = extractFilename();
        String fileUrl = FileUploadUtils.getPathFileName(BasicConfig.getDownloadCenterPath(), realFileName);
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(StyleUtils.getHeadStyle(), StyleUtils.getContentStyle());
        try {
            if (clazz != null && columnNames != null) {
                EasyExcel.write(FileUploadUtils.getAbsoluteFile(BasicConfig.getDownloadCenterPath(), realFileName), clazz).registerWriteHandler(horizontalCellStyleStrategy).includeColumnFieldNames(columnNames).sheet(sheetName).doWrite(data);
            } else if (clazz != null) {
                EasyExcel.write(FileUploadUtils.getAbsoluteFile(BasicConfig.getDownloadCenterPath(), realFileName), clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet(sheetName).doWrite(data);
            } else {
                EasyExcel.write(FileUploadUtils.getAbsoluteFile(BasicConfig.getDownloadCenterPath(), realFileName)).registerWriteHandler(horizontalCellStyleStrategy).head(heads.stream().map(Arrays::asList).collect(Collectors.toList())).sheet(sheetName).doWrite(data);
            }
        } catch (Exception e) {
            log.error("导出Excel失败，原因：", e);
            throw new RuntimeException("导出Excel失败，请联系管理员");
        }
        return fileUrl;
    }

    /**
     * 验证输入参数
     * @param clazz 数据对象的Class
     * @param sheetName sheet名
     * @param beanList Sheet信息列表
     */
    private static void validateInput(Class<?> clazz, String sheetName, List<SheetInfoBean> ... beanList) {
        if (clazz != null && sheetName == null) {
            throw new IllegalArgumentException("sheet名不能为空");
        }
        if (beanList != null && beanList.length > 0 && beanList[0] != null && !beanList[0].isEmpty()) {
            for (SheetInfoBean bean : beanList[0]) {
                if (bean.getHeadClass() == null || bean.getDataList() == null) {
                    throw new IllegalArgumentException("Sheet信息中的表头类或数据列表不能为空");
                }
            }
        }
    }

    /**
     * 导出Excel重新命名
     */
    public static String extractFilename() {
        return StringUtils.format("{}/{}.{}", DateUtils.datePath(),ULID.getId(), "xlsx");
    }

    /**
     * 处理合并单元格
     * @param data               解析数据
     * @param extraMergeInfoList 合并单元格信息
     * @param headRowNumber      起始行
     * @return 填充好的解析数据
     */
    public static <T> List<T> explainMergeData(List<T> data, List<CellExtra> extraMergeInfoList, Integer headRowNumber) {
        // 创建一个集合来存储已经处理过的行索引
        Set<Integer> processedRows = new HashSet<>();
        // 循环所有合并单元格信息
        for (CellExtra cellExtra : extraMergeInfoList) {
            if (cellExtra.getRowIndex() < headRowNumber) {
                continue;
            }
            int firstRowIndex = cellExtra.getFirstRowIndex() - headRowNumber;
            int lastRowIndex = cellExtra.getLastRowIndex() - headRowNumber;
            int firstColumnIndex = cellExtra.getFirstColumnIndex();
            int lastColumnIndex = cellExtra.getLastColumnIndex();
            // 获取初始值
            Object initValue = getInitValueFromList(firstRowIndex, firstColumnIndex, data);
            String mark = ULID.getId();
            // 设置值
            for (int i = firstRowIndex; i <= lastRowIndex; i++) {
                for (int j = firstColumnIndex; j <= lastColumnIndex; j++) {
                    setInitValueToList(initValue, i, j, data);

                }
                // 添加标识字段
                setMarkField(data.get(i), mark);
                // 标记该行已被处理
                processedRows.add(i);
            }
        }

        // 处理未合并的行
        for (int i = 0; i < data.size(); i++) {
            if (!processedRows.contains(i)) {
                String mark = ULID.getId();
                setMarkField(data.get(i), mark);
            }
        }
        return data;
    }

    /**
     * 设置合并单元格的标识字段
     *
     * @param object 数据对象
     * @param mark 标识值
     */
    private static void setMarkField(Object object, String mark) {
        if (object == null) return;

        for (Field field : object.getClass().getDeclaredFields()) {
            // 提升反射性能，关闭安全检查
            field.setAccessible(true);
            if ("mark".equals(field.getName())) {
                try {
                    field.set(object, mark);
                    break;
                } catch (IllegalAccessException e) {
                    log.error("设置合并单元格的标识字段异常：{}", e.getMessage());
                }
            }
        }
    }

    /**
     * 设置合并单元格的值
     *
     * @param filedValue  值
     * @param rowIndex    行
     * @param columnIndex 列
     * @param data        解析数据
     */
    private static <T> void setInitValueToList(Object filedValue, Integer rowIndex, Integer columnIndex, List<T> data) {
        if (rowIndex >= data.size()) return;

        T object = data.get(rowIndex);

        for (Field field : object.getClass().getDeclaredFields()) {
            // 提升反射性能，关闭安全检查
            field.setAccessible(true);
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                if (annotation.index() == columnIndex) {
                    try {
                        field.set(object, filedValue);
                        break;
                    } catch (IllegalAccessException e) {
                        log.error("设置合并单元格的值异常：{}", e.getMessage());
                    }
                }
            }
        }
    }

    /**
     * 获取合并单元格的初始值
     * rowIndex对应list的索引
     * columnIndex对应实体内的字段
     *
     * @param firstRowIndex    起始行
     * @param firstColumnIndex 起始列
     * @param data             列数据
     * @return 初始值
     */
    private static <T> Object getInitValueFromList(Integer firstRowIndex, Integer firstColumnIndex, List<T> data) {
        Object filedValue = null;
        T object = data.get(firstRowIndex);
        for (Field field : object.getClass().getDeclaredFields()) {
            // 提升反射性能，关闭安全检查
            field.setAccessible(true);
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                if (annotation.index() == firstColumnIndex) {
                    try {
                        filedValue = field.get(object);
                        break;
                    } catch (IllegalAccessException e) {
                        log.error("设置合并单元格的初始值异常：{}", e.getMessage());
                    }
                }
            }
        }
        return filedValue;
    }

}
